package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartAsyncService;
import com.atguigu.gmall.cart.service.CartInfoService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Fxz
 * @version 1.0
 * @date 2022-02-16 13:43
 */
@SuppressWarnings("all")
@Service
@RequiredArgsConstructor
public class CartInfoServiceImpl implements CartInfoService {

	/**
	 * 此处添加了泛型 按照类型匹配会匹配不到 使用@Resource注解按照name注入
	 *
	 * @see <a href=
	 * "https://stackoverflow.com/questions/53138537/consider-revisiting-the-entries-above-or-defining-a-bean-of-type-org-springfram"/>
	 * @see <a href="https://blog.csdn.net/zhaoheng314/article/details/81564166"/>
	 */
	@Resource
	private RedisTemplate<String, Object> redisTemplate;

	private final CartInfoMapper cartInfoMapper;

	private final CartAsyncService cartAsyncService;

	private final ProductFeignClient productFeignClient;

	/**
	 * 添加购物车
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void addToCart(Long skuId, String userId, Integer skuNum) {
		// 定义缓存key
		String cartKey = getCartKey(userId);
		// 先查询缓存中是否有改key的数据 有的话说明缓存中数据是全的 没有的话说明可能是已经过期了,此时直接加入可能会造成数据不一致
		if (redisTemplate.hasKey(cartKey)) {
			// 没有key 重新加载一次
			this.loadCartCache(userId);
		}
		// 跟据skuId和userId查询购物车信息
		CartInfo cartInfo = (CartInfo) redisTemplate.boundHashOps(cartKey.toString()).get(skuId.toString());

		// 如果购物车中存在当前商品 添加商品数量 否则查询商品信息并新增到数据库
		if (ObjectUtils.isNotEmpty(cartInfo)) {
			// 商品数量相加
			cartInfo.setSkuNum(cartInfo.getSkuNum() + skuNum);
			// 选中
			cartInfo.setIsChecked(1);
			// 查询当前商品实时价格并赋值给购物车
			cartInfo.setSkuPrice(productFeignClient.getSkuPrice(skuId));
			// 更新时间
			cartInfo.setUpdateTime(new Timestamp(System.currentTimeMillis()));
			// 异步更新数据库
			cartAsyncService.updateCartInfo(cartInfo);
			// 更新缓存
			redisTemplate.opsForHash().put(cartKey, skuId.toString(), cartInfo);
		}
		else {
			SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
			CartInfo cart = new CartInfo().setUserId(userId).setSkuId(skuId).setCartPrice(skuInfo.getPrice())
					.setSkuPrice(skuInfo.getPrice()).setSkuNum(skuNum).setSkuName(skuInfo.getSkuName())
					.setImgUrl(skuInfo.getSkuDefaultImg()).setCreateTime(new Timestamp(System.currentTimeMillis()))
					.setUpdateTime(new Timestamp(System.currentTimeMillis()));
			// 异步保存到数据库
			cartAsyncService.saveCartInfo(cart);
			// 保存到缓存
			redisTemplate.opsForHash().put(cartKey, skuId.toString(), cart);
		}
		// 给缓存设置过期时间
		setExpire(cartKey);
	}

	/**
	 * 查询购物车
	 */
	@Override
	public List<CartInfo> getCartList(String userId, String userTempId) {
		List<CartInfo> cartInfoList = new ArrayList<>();

		// 查缓存
		if (StringUtils.isNotBlank(userId) && StringUtils.isBlank(userTempId)) {
			// 登录但是没有临时购物车 则直接返回购物车信息
			cartInfoList = getCartList(userId);
		}
		else if (StringUtils.isNotBlank(userId)) {
			// 登录且有临时购物车 合并
			List<CartInfo> tempCartList = getCartList(userTempId);
			cartInfoList = getCartList(userId);
			if (CollectionUtils.isNotEmpty(tempCartList)) {
				// 临时购物车有值 合并购物车
				cartInfoList = mergeToCartList(cartInfoList, tempCartList, userId);
				// 删除临时购物车数据
				deleteCartList(userTempId);
			}
		}
		else if (StringUtils.isNotBlank(userTempId)) {
			// 没登陆 查询临时购物车
			cartInfoList = getCartList(userTempId);
		}

		return cartInfoList;
	}

	/**
	 * 更新选中状态
	 */
	@Override
	public void checkCart(String userId, Integer isChecked, Long skuId) {
		// 操作数据库
		cartAsyncService.checkCart(userId, isChecked, skuId);
		// 同步redis
		String cartKey = getCartKey(userId);
		CartInfo cartInfo = (CartInfo) redisTemplate.opsForHash().get(cartKey, skuId.toString());
		Optional.ofNullable(cartInfo).ifPresent((cart) -> {
			cartInfo.setIsChecked(isChecked);
			redisTemplate.opsForHash().put(cartKey, skuId.toString(), cart);
			setExpire(cartKey);
		});
	}

	/**
	 * 删除购物项
	 */
	@Override
	public void deleteCartInfo(String userId, Long skuId) {
		cartAsyncService.deleteCartInfo(userId, skuId);
		redisTemplate.opsForHash().delete(getCartKey(userId), skuId.toString());
	}

	/**
	 * 跟据用户id 获取购物车列表(已选中)
	 */
	@Override
	public List<CartInfo> getCartCheckedList(Long userId) {
		List cartInfoList = redisTemplate.opsForHash().values(getCartKey(userId.toString()));
		if (CollectionUtils.isNotEmpty(cartInfoList)) {
			List collect = (List) cartInfoList.stream().filter(cartInfo -> ((CartInfo) cartInfo).getIsChecked() == 1)
					.collect(Collectors.toList());
		}
		return cartInfoList;
	}

	/**
	 * 删除购物车
	 */
	private void deleteCartList(String userTempId) {
		// 删除数据库
		cartAsyncService.deleteCartInfo(userTempId);
		// 删除redis
		redisTemplate.delete(getCartKey(userTempId));
	}

	/**
	 * 合并购物车
	 */
	private List<CartInfo> mergeToCartList(List<CartInfo> cartInfoList, List<CartInfo> tempCartList, String userId) {
		Map<Long, CartInfo> longCartInfoMap = cartInfoList.stream()
				.collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
		tempCartList.stream().forEach(item -> {
			if (longCartInfoMap.containsKey(item.getSkuId())) {
				CartInfo cartInfo = longCartInfoMap.get(item.getSkuId());
				cartInfo.setSkuNum(cartInfo.getSkuNum() + item.getSkuNum());
				cartInfo.setUpdateTime(new Timestamp(System.currentTimeMillis()));
				// 选中状态
				if (item.getIsChecked().longValue() == 1) {
					cartInfo.setIsChecked(1);
				}
				cartInfoMapper.update(cartInfo, Wrappers.<CartInfo>lambdaUpdate()
						.eq(CartInfo::getSkuId, cartInfo.getSkuId()).eq(CartInfo::getUserId, cartInfo.getUserId()));
			}
			else {
				item.setUserId(userId);
				item.setCreateTime(new Timestamp(System.currentTimeMillis()));
				item.setUpdateTime(new Timestamp(System.currentTimeMillis()));
				cartInfoMapper.insert(item);
			}
		});
		return loadCartCache(userId);
	}

	/**
	 * 查询购物车
	 */
	public List<CartInfo> getCartList(String userId) {
		// 购物车key
		String cartKey = this.getCartKey(userId);
		// 获取缓存数据
		List values = redisTemplate.opsForHash().values(cartKey);
		// 判断当前集合是否为空
		if (CollectionUtils.isEmpty(values)) {
			// 缓存中没有数据 从数据库查询
			values = this.loadCartCache(userId);
		}
		if (CollectionUtils.isNotEmpty(values)) {
			// 按照购物车中商品添加的更新时间排序
			values.sort((o1, o2) -> DateUtil.truncatedCompareTo(((CartInfo) o2).getUpdateTime(),
					((CartInfo) o1).getUpdateTime(), Calendar.SECOND));
		}
		return values;
	}

	/**
	 * 跟据userId查询数据库购物车信息
	 */
	public List loadCartCache(String userId) {
		String cartKey = this.getCartKey(userId);
		// 查询购物车信息
		List<CartInfo> cartInfoList = cartInfoMapper
				.selectList(Wrappers.<CartInfo>query().lambda().eq(CartInfo::getUserId, userId));
		if (CollectionUtils.isNotEmpty(cartInfoList)) {
			cartInfoList.forEach(item -> {
				item.setSkuPrice(productFeignClient.getSkuPrice(item.getSkuId()));
				redisTemplate.opsForHash().put(cartKey, item.getSkuId().toString(), item);
			});
		}
		// 设置缓存过期时间
		this.setExpire(cartKey);
		return cartInfoList;
	}

	/**
	 * 定义缓存key
	 */
	private String getCartKey(String userId) {
		return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
	}

	/**
	 * 给缓存设置过期时间
	 */
	private void setExpire(String key) {
		redisTemplate.expire(key, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
	}

}
